// Fill out your copyright notice in the Description page of Project Settings.


#include "SCookDialogue.h"
#include "SlateOptMacros.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Layout/SExpandableArea.h"
#include "Widgets/Shared/SPakPlatformsView.h"
#include "Widgets/Shared/SPakAssetsView.h"
#include "Widgets/Shared/SElectAssetsView.h"
#include "AssetRegistryModule.h"
#include "HotPakerFileLib.h"
#include "HAL/PlatformFilemanager.h"
#include "Widgets/Input/SButton.h"
#include "Interfaces/ITargetPlatformManagerModule.h"
#include "Interfaces/ITargetPlatform.h"
#include "Editor.h"
#include "PakUtils/StateThread.hpp"
#include "Async/ParallelFor.h"
#include "CookerSettings.h"
#include "UnrealEdMisc.h"
#include "UATHelper/Public/IUATHelperModule.h"
#include "Settings/ProjectPackagingSettings.h"


#define LOCTEXT_NAMESPACE "SCookDialogue"

BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SCookDialogue::Construct(const FArguments& InArgs)
{
	IsCooking = false;

	ChildSlot
		[
			SNew(SVerticalBox)

			+ SVerticalBox::Slot()
		.AutoHeight()
		[
			SNew(SHorizontalBox)

			+ SHorizontalBox::Slot()
		.FillWidth(1.0)
		.VAlign(VAlign_Center)
		[
			SNew(STextBlock)
			.Text(LOCTEXT("Cook Command", "Cook Asset By Tool"))
		]
		]

	+ SVerticalBox::Slot()
		.AutoHeight()
		.Padding(0.f, 8.f, 0.f, 0.f)
		[
			SNew(SExpandableArea)
			.AreaTitle(LOCTEXT("CookPlatforms", "Platforms"))
		.InitiallyCollapsed(true)
		.Padding(8.0)
		.BodyContent()
		[
			SNew(SVerticalBox)
			+ SVerticalBox::Slot()
		.AutoHeight()
		[
			SAssignNew(PakPlatformsView, SPakPlatformsView)
		]
		]
		]

	+ SVerticalBox::Slot()
		.AutoHeight()
		.Padding(0.f, 8.f, 0.f, 0.f)
		[
			SNew(SExpandableArea)
			.AreaTitle(LOCTEXT("CookAssets", "Assets"))
		.InitiallyCollapsed(true)
		.Padding(8.f)
		.BodyContent()
		[
			SNew(SVerticalBox)
			+ SVerticalBox::Slot()
		.AutoHeight()
		[
			SAssignNew(PakAssetsView, SPakAssetsView)
		]
		]
		]

	+ SVerticalBox::Slot()
		.AutoHeight()
		.HAlign(HAlign_Right)
		.Padding(0.f, 8.f, 0.f, 0.f)
		[
			SNew(SHorizontalBox)

			+ SHorizontalBox::Slot()
		.AutoWidth()
		.HAlign(HAlign_Center)
		.Padding(10.f, 0.f, 0.f, 0.f)
		[
			SNew(SButton)
			.OnClicked(PakAssetsView.Get(), &SPakAssetsView::SelectAllAssets)
		.HAlign(HAlign_Center)
		.VAlign(VAlign_Center)
		[
			SNew(STextBlock)
			.Text(LOCTEXT("SelectAllAssets", "SelectAll"))
		]
		]

	+ SHorizontalBox::Slot()
		.AutoWidth()
		.HAlign(HAlign_Center)
		.Padding(10.f, 0.f, 0.f, 0.f)
		[
			SNew(SButton)
			.OnClicked(PakAssetsView.Get(), &SPakAssetsView::SelectRootAssets)
		.HAlign(HAlign_Center)
		.VAlign(VAlign_Center)
		[
			SNew(STextBlock)
			.Text(LOCTEXT("SelectRootAssets", "SelectRoot"))
		]
		]

	+ SHorizontalBox::Slot()
		.AutoWidth()
		.HAlign(HAlign_Center)
		.Padding(10.f, 0.f, 0.f, 0.f)
		[
			SNew(SButton)
			.OnClicked(PakAssetsView.Get(), &SPakAssetsView::ClearSelectAssets)
		.HAlign(HAlign_Center)
		.VAlign(VAlign_Center)
		[
			SNew(STextBlock)
			.Text(LOCTEXT("SelectNoneAssets", "ClearSelect"))
		]
		]


		]


	+ SVerticalBox::Slot()
		.AutoHeight()
		.Padding(0.f, 8.f, 0.f, 0.f)
		[
			SNew(SExpandableArea)
			.AreaTitle(LOCTEXT("CookAssets", "Assets"))
		.InitiallyCollapsed(true)
		.Padding(8.f)
		.BodyContent()
		[
			SNew(SVerticalBox)
			+ SVerticalBox::Slot()
		.AutoHeight()
		[
			SAssignNew(ElectAssetsView, SElectAssetsView)
		]
		]
		]



	+SVerticalBox::Slot()
		.AutoHeight()
		.HAlign(HAlign_Right)
		.Padding(0.f, 8.f, 0.f, 0.f)
		[
			SNew(SHorizontalBox)

			+ SHorizontalBox::Slot()
		.AutoWidth()
		.HAlign(HAlign_Center)
		.Padding(10.f, 0.f, 0.f, 0.f)
		[
			SNew(SButton)
			.OnClicked(this, &SCookDialogue::CookSelectAssets)
		.IsEnabled_Lambda([this]() { return PakAssetsView->GetAllSelectAssets().Num() > 0 && PakPlatformsView->GetAllSelectPlatforms().Num() > 0 && IsCooking == false;  })
		.HAlign(HAlign_Center)
		.VAlign(VAlign_Center)
		[
			SNew(STextBlock)
			.Text(LOCTEXT("CookSelectAssets", "CookAssets"))
		]
		]

		]



		];

	PakAssetsView->SelectAssetsChangeDelegate.BindRaw(ElectAssetsView.Get(), &SElectAssetsView::SelectAssetsChange);

}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION

void SCookDialogue::CookPaths(TArray<FString> SelectedPaths)
{
	if (IsCooking)
		return;

	IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();

	TArray<FString> TotalPaths;
	for (auto SelectPath : SelectedPaths)
	{
		FPakerFileSearchVisitor Visitor;
		PlatformFile.IterateDirectoryRecursively(*UHotPakerFileLib::ConverPackagePathToFullPath(SelectPath), Visitor);
		for (auto SearchPath : Visitor.Directories)
			TotalPaths.AddUnique(SearchPath);
	}

	FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
	TArray<FAssetData> AssetDatas;
	for (auto CookPath : TotalPaths)
	{
		//UE_LOG(LogTemp, Log, TEXT("Find Package Path %s"), *UHotPakerFileLib::FullPathToPackage(CookPath));

		TArray<FAssetData> OutAssetDatas;
		AssetRegistryModule.Get().GetAssetsByPath(FName(*UHotPakerFileLib::ConverFullPathToPackagePath(CookPath)), OutAssetDatas);

		for (auto AssetData : OutAssetDatas)
			if (!AssetData.IsRedirector())
				AssetDatas.AddUnique(AssetData);
	}
	PakAssetsView->AssignAssetList(AssetDatas);
	PakPlatformsView->ClearAllSelectPlatforms();
}

void SCookDialogue::CookAssets(TArray<FAssetData> SelectedAssets)
{
	if (IsCooking)
		return;

	PakAssetsView->AssignAssetList(SelectedAssets);
	PakPlatformsView->ClearAllSelectPlatforms();
}

void SCookDialogue::DoCookAsset()
{
#if 0
	TArray<FAssetData> AssetDatas;
	if (UHotPakerFileLib::GetAssetDatasByPackageNames(PakAssetsView->GetAllSelectAssets(), AssetDatas))
	{
		uint32 SaveFlags = SAVE_KeepGUID | SAVE_Async | SAVE_Unversioned;
		TArray<ITargetPlatform*> TargetPlatforms;
		TArray<ITargetPlatform*> AllPlatforms = GetTargetPlatformManager()->GetTargetPlatforms();
		for (auto TargetPlatform : AllPlatforms)
		{
			if (PakPlatformsView->GetAllSelectPlatforms().Contains(TargetPlatform->PlatformName()))
				TargetPlatforms.Add(TargetPlatform);
		}
		for (auto TargetPlatform : TargetPlatforms)
		{
			for (auto AssetData : AssetDatas)
			{
				FString SavePath;
				if (UHotPakerFileLib::ConverPackageNameToSavePath(AssetData.PackageName, TEXT("D:/Projects/Output/Cooks"), TargetPlatform->PlatformName(), SavePath))
				{
					UPackage::Save(AssetData.GetPackage(), nullptr, RF_Public, *SavePath, GError, NULL, false, false, SaveFlags, TargetPlatform, FDateTime::MinValue(), false);
					//GEditor->Save(AssetData.GetPackage(), nullptr, RF_NoFlags, *SavePath, GError, NULL, false, false, SaveFlags, TargetPlatform, FDateTime::MinValue(), false);

					UE_LOG(LogTemp, Log, TEXT("Cooking Platform[%s]  Asset[%s]"), *TargetPlatform->PlatformName(), *AssetData.GetPackage()->GetName());
				}
			}
		}
	}
	//#else
	uint32 SaveFlags = SAVE_KeepGUID | SAVE_Async | SAVE_Unversioned;
	TArray<ITargetPlatform*> TargetPlatforms;
	TArray<ITargetPlatform*> AllPlatforms = GetTargetPlatformManager()->GetTargetPlatforms();
	for (auto TargetPlatform : AllPlatforms)
	{
		if (PakPlatformsView->GetAllSelectPlatforms().Contains(TargetPlatform->PlatformName()))
			TargetPlatforms.Add(TargetPlatform);
	}
	for (auto TargetPlatform : TargetPlatforms)
	{
		for (TObjectIterator<UPackage> It; It; ++It)
		{
			UPackage* Package = *It;

			if (Package->GetOuter() == nullptr && (Package->GetName().StartsWith(TEXT("/Engine")) || Package->GetName().StartsWith(TEXT("/Game"))))
			{
				if (Package->GetName().Contains(TEXT("M_UE4Man_ChestLogo")))
				{
					int a = 10;
					UE_LOG(LogTemp, Log, TEXT("Find M_UE4Man_ChestLogo"));
				}

				FString SavePath;
				if (UHotPakerFileLib::ConverPackageNameToSavePath(Package->GetFName(), TEXT("D:/Projects/Output/Cooks"), TargetPlatform->PlatformName(), SavePath))
				{
					int b = 10 * SavePath.Len();

					FSavePackageResultStruct SavePackageResult = UPackage::Save(Package, nullptr, RF_Public, *SavePath, GError, NULL, false, false, SaveFlags, TargetPlatform, FDateTime::MinValue(), false);

					if (SavePackageResult.Result == ESavePackageResult::Success)
					{
						UE_LOG(LogTemp, Log, TEXT("Cooking Succeed Platform[%s]  Asset[%s]"), *TargetPlatform->PlatformName(), *Package->GetName());
					}
					else
					{
						UE_LOG(LogTemp, Log, TEXT("[%d] Cooking Failed Platform[%s]  Asset[%s]"), b, *TargetPlatform->PlatformName(), *Package->GetName());
					}
				}
			}
		}
	}
#endif

	TArray<FAssetData> AssetDatas;
	if (UHotPakerFileLib::GetAssetDatasByPackageNames(PakAssetsView->GetAllSelectAssets(), AssetDatas))
	{
		TArray<ITargetPlatform*> TargetPlatforms;
		TArray<ITargetPlatform*> AllPlatforms = GetTargetPlatformManager()->GetTargetPlatforms();
		for (auto TargetPlatform : AllPlatforms)
		{
			if (PakPlatformsView->GetAllSelectPlatforms().Contains(TargetPlatform->PlatformName()))
				TargetPlatforms.Add(TargetPlatform);
		}

		//ParallelFor(AssetDatas.Num(), [this, &AssetDatas, &TargetPlatforms](int32 PackageIdx)
		for (int PackageIdx = 0; PackageIdx < AssetDatas.Num(); ++PackageIdx)
		{
			UPackage* Package = AssetDatas[PackageIdx].GetPackage();
			check(Package);

			EObjectFlags FlagsToCook = RF_Public;
			TArray<UObject*> ObjsInPackage;
			UWorld* World = nullptr;
			{
				//SCOPE_TIMER(SaveCookedPackage_FindWorldInPackage);
				GetObjectsWithOuter(Package, ObjsInPackage, false);
				for (UObject* Obj : ObjsInPackage)
				{
					World = Cast<UWorld>(Obj);
					if (World)
					{
						FlagsToCook = RF_NoFlags;
						break;
					}
				}
			}

			uint32 OriginalPackageFlags = Package->GetPackageFlags();

			for (auto TargetPlatform : TargetPlatforms)
			{
				FString SavePath;
				if (UHotPakerFileLib::ConverPackageNameToSavePath(Package->GetFName(), TEXT("D:/Projects/Output/Cooks"), TargetPlatform->PlatformName(), SavePath))
				{
					bool bSwap = (!TargetPlatform->IsLittleEndian()) ^ (!PLATFORM_LITTLE_ENDIAN);
					uint32 SaveFlags = SAVE_KeepGUID | SAVE_Async | SAVE_ComputeHash;
					if (!TargetPlatform->HasEditorOnlyData())
					{
						Package->SetPackageFlags(PKG_FilterEditorOnly);
					}
					else
					{
						Package->ClearPackageFlags(PKG_FilterEditorOnly);
					}


					GEditor->Save(Package, World, FlagsToCook, *SavePath, GError, NULL, bSwap, false, SaveFlags, TargetPlatform, FDateTime::MinValue(), false);

					//UPackage::Save(Package, nullptr, RF_Public, *SavePath, GError, NULL, false, false, SaveFlags, TargetPlatform, FDateTime::MinValue(), false);

					UE_LOG(LogTemp, Log, TEXT("Cooking Platform[%s]  Asset[%s]"), *TargetPlatform->PlatformName(), *Package->GetName());
				}
			}

			Package->SetPackageFlagsTo(OriginalPackageFlags);
		}
		//);
	}



}


FReply SCookDialogue::CookSelectAssets()
{
#if 0
	//CookThread = MakeShareable(new FStateThread(TEXT("CookThread"), [this]() { DoCookAsset();}));

	CookThread = MakeUnique<FStateThread>(TEXT("CookThread"), [this]() { DoCookAsset(); });

	CookThread->StateRun(this, &SCookDialogue::CookRun)
		.StateComplete(this, &SCookDialogue::CookComplete)
		.StateStop(this, &SCookDialogue::CookStop)
		.StateJoin(this, &SCookDialogue::CookJoin)
		.StateExit(this, &SCookDialogue::CookExit)
		.Execute();
	//#else
	DoCookAsset();
#endif


	FString OptionalParams;

	OptionalParams += TEXT(" -targetplatform=");
	OptionalParams += TEXT("Win64");

	const UProjectPackagingSettings* const PackagingSettings = GetDefault<UProjectPackagingSettings>();

	if (PackagingSettings->bSkipEditorContent)
	{
		OptionalParams += TEXT(" -SkipCookingEditorContent");
	}


	UCookerSettings const* CookerSettings = GetDefault<UCookerSettings>();
	if (CookerSettings->bIterativeCookingForFileCookContent)
	{
		OptionalParams += TEXT(" -iterate");
	}

	FString ProjectPath = FPaths::IsProjectFilePathSet() ? FPaths::ConvertRelativePathToFull(FPaths::GetProjectFilePath()) : FPaths::RootDir() / FApp::GetProjectName() / FApp::GetProjectName() + TEXT(".uproject");
	FString CommandLine = FString::Printf(TEXT("-ScriptsForProject=\"%s\" BuildCookRun %s%s -nop4 -project=\"%s\" -cook -skipstage -ue4exe=\"%s\" %s -utf8output"),
		*ProjectPath,
		(FApp::GetEngineIsPromotedBuild() || FApp::IsEngineInstalled()) ? TEXT("-nocompile -nocompileeditor") : TEXT("-nocompileeditor"),
		FApp::IsEngineInstalled() ? TEXT(" -installed") : TEXT(""),
		*ProjectPath,
		*FUnrealEdMisc::Get().GetExecutableForCommandlets(),
		*OptionalParams
	);


	UE_LOG(LogTemp, Log, TEXT("CommandLine [%s]"), *CommandLine);

	IUATHelperModule::Get().CreateUatTask(CommandLine, FText::FromString(TEXT("CookingContent")), LOCTEXT("CookingContentTaskName", "Cooking content"), LOCTEXT("CookingTaskName", "Cooking"), FEditorStyle::GetBrush(TEXT("MainFrame.CookContent")));


	return FReply::Handled();
}



void SCookDialogue::CookRun()
{
	IsCooking = true;
	UE_LOG(LogTemp, Log, TEXT("Cook Run"));
}

void SCookDialogue::CookComplete()
{
	IsCooking = false;
	UE_LOG(LogTemp, Log, TEXT("Cook Complete"));
}

void SCookDialogue::CookStop()
{

}

void SCookDialogue::CookJoin()
{

}

void SCookDialogue::CookExit()
{

}

#undef LOCTEXT_NAMESPACE
